package com.tensai.teach.shudu;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author Tensai
 */
public class ShuDu {

    static int[][] sd = {
            {6, 8, 0,   3, 5, 0,   0, 0, 0},
            {0, 0, 0,   0, 1, 0,   0, 2, 0},
            {0, 0, 7,   0, 0, 0,   9, 0, 0},

            {1, 0, 0,   0, 0, 0,   0, 0, 0},
            {3, 0, 5,   0, 9, 0,   1, 0, 2},
            {0, 0, 0,   0, 0, 0,   0, 0, 9},

            {0, 0, 3,   0, 2, 0,   8, 0, 0},
            {0, 6, 0,   0, 7, 0,   0, 0, 0},
            {0, 0, 0,   0, 4, 5,   0, 3, 1}
    };

    static Set<Integer>[] h = new HashSet[9];
    static Set<Integer>[] s = new HashSet[9];
    static Set<Integer>[][] k = new HashSet[3][3];

    static Set<Integer>[][] back = new HashSet[9][9];


    public static void main(String[] args) {
        makeH();
        makeK();
        makeS();
        boolean flag = false;
        do {
            int t = 81;
            do {
                t = makeBack();
            } while (t != 0);
            flag = andOfOnly();
        } while (flag);
        Arrays.stream(sd).forEach(item -> System.out.println(Arrays.toString(item)));
    }

    static void makeH() {
        for (int i = 0; i < 9; i++) {
            Set<Integer> set = IntStream.range(1, 10).boxed().collect(Collectors.toSet());
            for (int j = 0; j < 9; j++) {
                if (sd[i][j] != 0) {
                    set.remove(sd[i][j]);
                }
            }
            h[i] = set;
        }
    }

    static void makeS() {
        for (int i = 0; i < 9; i++) {
            Set<Integer> set = IntStream.range(1, 10).boxed().collect(Collectors.toSet());
            for (int j = 0; j < 9; j++) {
                if (sd[j][i] != 0) {
                    set.remove(sd[j][i]);
                }
            }
            s[i] = set;
        }
    }

    static void makeK() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                Set<Integer> set = IntStream.range(1, 10).boxed().collect(Collectors.toSet());
                for (int l = i * 3; l < i * 3 + 3; l++) {
                    for (int m = j * 3; m < j * 3 + 3; m++) {
                        if (sd[l][m] != 0) {
                            set.remove(sd[l][m]);
                        }
                    }
                }
                k[i][j] = set;
            }
        }
    }

    static int makeBack() {
        int p = 0;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (sd[i][j] == 0) {
                    Set<Integer> set = IntStream.range(1, 10).boxed().collect(Collectors.toSet());
                    set.retainAll(h[i]);
                    set.retainAll(s[j]);
                    set.retainAll(k[i / 3][j / 3]);
                    if (set.size() == 1) {
                        int finalI = i;
                        int finalJ = j;
                        set.forEach(integer -> {
                            sd[finalI][finalJ] = integer;
                            System.out.println(">>>>" + finalI + ", " + finalJ + "\t" + integer);
                        });
                        h[i].removeAll(set);
                        reCheckBack(true, i, set);
                        s[j].removeAll(set);
                        reCheckBack(false, j, set);
                        k[i / 3][j / 3].removeAll(set);
                        p++;
                    } else {
                        back[i][j] = set;
                    }
                }
            }
        }
        return p;
    }

    /**
     * @param b 是否横向
     */
    static void reCheckBack(boolean b, int t, Set<Integer> set) {
        for (int i = 0; i < 9; i++) {
            if (b) {
                if (back[t][i] != null) {
                    back[t][i].removeAll(set);
                }
            } else {
                if (back[i][t] != null) {

                    back[i][t].removeAll(set);
                }
            }
        }
    }

    static boolean andOfOnly() {
        AtomicBoolean flag = new AtomicBoolean(false);
        for (int i = 0; i < 9; i++) {
            Set<Integer> set = new HashSet<>();
            for (int j = 0; j < 9; j++) {
                if (back[i][j] != null) {
                    set.addAll(back[i][j]);
                }
            }
            int finalI = i;
            set.forEach(item -> {
                int c = 0;
                int y = 0;
                for (int j = 0; j < 9; j++) {
                    if (back[finalI][j] != null && back[finalI][j].contains(item)) {
                        c++;
                        y = j;
                    }
                }
                if (c == 1) {
                    sd[finalI][y] = item;
                    back[finalI][y] = null;
                    System.out.println("=====" + finalI + ", " + y + "\t" + item);
                    h[finalI].remove(item);
                    s[y].remove(item);
                    k[finalI / 3][y / 3].remove(item);
                    flag.set(true);
                }
            });
        }
        return flag.get();
    }
}

